#ifndef TIGER_COMPILER_COLOR_H
#define TIGER_COMPILER_COLOR_H

#include "tiger/codegen/assem.h"
#include "tiger/frame/temp.h"
#include "tiger/liveness/liveness.h"
#include "tiger/util/graph.h"
#include <map>
#include <set>

namespace col {
struct Result {
    Result() : coloring(nullptr), spills(nullptr) {}
    Result(temp::Map *coloring, live::INodeListPtr spills) : coloring(coloring), spills(spills) {}
    temp::Map *coloring;
    live::INodeListPtr spills;
};

class Color {
    /* TODO: Put your lab6 code here */
public:
    Color(live::LiveGraph ln, temp::TempList *noSpillTemps) : liveness(ln), noSpillTemp(noSpillTemps) {}
    col::Result doColoring();

private:
    live::LiveGraph liveness;

    graph::Table<temp::Temp, live::MoveList> moveList;

    live::MoveList *worklistMoves;

    temp::Map *coloring;

    std::set<std::pair<live::INodePtr, live::INodePtr>> adjSet;

    std::map<live::INodePtr, graph::NodeList<temp::Temp>> adjList;

    std::map<live::INodePtr, int> degree;

    graph::NodeList<temp::Temp> spillWorklist;

    graph::NodeList<temp::Temp> simplifyWorklist;

    graph::NodeList<temp::Temp> freezeWorklist;

    live::MoveList activeMoves;

    graph::NodeList<temp::Temp> selectStack;

    graph::NodeList<temp::Temp> coalescedNodes;

    tab::Table<live::INode, live::INode> alias;

    live::MoveList coalescedMoves;

    live::MoveList constrainedMoves;

    live::MoveList frozenMoves;

    temp::TempList *noSpillTemp;

    graph::NodeList<temp::Temp> coloredNodes;

    graph::NodeList<temp::Temp> *spilledNodes;

    void init();
    void Build();
    void AddEdge(live::INodePtr u, live::INodePtr v);
    bool precolored(temp::Temp *t);
    void MakeWorklist();
    live::INodeListPtr Adjacent(live::INodePtr node);
    live::MoveList *NodeMoves(live::INodePtr node);
    bool MoveRelated(live::INodePtr node);
    void Simplify();
    void DecrementDegree(live::INodePtr node);
    void EnableMoves(graph::NodeList<temp::Temp> *nodes);
    void Coalesce();
    live::INodePtr GetAlias(live::INodePtr node);
    void AddWorkList(live::INodePtr node);
    bool OK(live::INodePtr t, live::INodePtr r);
    bool OKForAll(live::INodeListPtr nodes, live::INodePtr r);
    bool Conservative(live::INodeListPtr nodes);
    void Combine(live::INodePtr u, live::INodePtr v);
    void Freeze();
    void FreezeMoves(live::INodePtr u);
    void SelectSpill();
    void AssignColors();

    void ShowStates();
};
} // namespace col

#endif // TIGER_COMPILER_COLOR_H
